package com.example.java_doc_searcher.seracher;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Parse {
    private Index index=new Index();
    //指定离线下载文档的位置
    private static final String INPUT_PATH="D:\\codeutil\\javadoc\\docs\\api\\";
    public void  parseHTML(File file) throws IOException {
        //解析标题
        String title=parseTitle(file);
        //解析网络地址
        String url=parseUrl(file);
        //解析文章内容
        //String content=parseContent(file);
        String content=parseContentByRegex(file);
        //TODO 添加索引
        index.addDoc(title,url,content);
    }
    public String parseContent(File file) throws IOException {
        //拼接文章内容
        //如果遇到<的时候就停止读取文章的内容，如果遇到>的时候开始读取文章的内容
        //在读取的时候还要判断，这个字符是不是一个有效字符
        StringBuilder content=new StringBuilder();
        Boolean isCopy=true;
        //这样写即使我们不关闭文件的读取时候，也会自动帮助我们关闭文件
        //不关闭，文件描述符会将内存占满，然后会造成内存泄漏
        try( FileReader fileReader=new FileReader(file)){
            while(true) {
                int ret=fileReader.read();
                if(ret==-1) {
                    break;
                }
                char ch=(char)ret;
                if(isCopy) {
                    if (ch=='<') {
                        isCopy=false;
                        continue;
                    }
                    if (ch=='\n' || ch=='\r') {
                        ch=' ';
                    }
                    content.append(ch);
                } else {
                    if(ch=='>') {
                        isCopy=true;
                    }
                }
            }
            return content.toString();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
    //这里要进行读取文件的全部内容
    public String readFile(File file) throws IOException {
        try(BufferedReader bufferedReader=new BufferedReader(new FileReader(file))) {
            StringBuilder content=new StringBuilder();
            while(true) {
                int ret=bufferedReader.read();
                if(ret==-1) {
                    break;
                }
                char ch=(char) ret;
                if(ch == '\n' || ch == '\r') {
                    ch=' ';
                }
                content.append(ch);
            }
            return content.toString();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
    public String parseContentByRegex(File file) throws IOException {
        String content=readFile(file);
        //这里先进行替换script标签
        //如果先进行替换script标签的时候的，导致script标签里面的内容在后续替换无法被去除掉。
        //之后又将多余空格替换成一个空格。
        content=content.replaceAll("<script.*?>(.*?)</script>"," ");
        content=content.replaceAll("<.*?>"," ");
        //将多余的空格替换成一个空格，用户在看着的时候体验感不好
        content=content.replaceAll("\\s+"," ");
        return content;
    }
    public String parseTitle(File file) {
        return file.getName().substring(0, file.getName().length()-".html".length());
    }
    public String parseUrl(File file) {
        //将url拼接成线上的IP地址
        String part1="https://docs.oracle.com/javase/8/docs/api/";
        //这点刚才有问题，如果两个相同的文件名字，但是不同的文件目录，显示的路径是相同的
        String part2= file.getAbsolutePath().substring(INPUT_PATH.length());
        return (part1+part2).replace('\\','/');
    }
    //这里我们就不用进行实验了，枚举文件消耗了大量的时间
    //使用多线程的方法

//    private void run() throws IOException {
//        //要进行构建方法
//        System.out.println("索引制作开始");
//        long begin=System.currentTimeMillis();
//        List<File> fileList=new ArrayList<>();
//        enumFile(INPUT_PATH,fileList);
//        //TODO 这里能拿到每个文件，然后将文件进行解析
//        //System.out.println(fileList);
//        for(File file:fileList) {
//            System.out.println("开始解析："+file.getAbsolutePath());
//            parseHTML(file);
//            //在parseHTML，中去构建索引，并打印时间
//        }
//        long end=System.currentTimeMillis();
//        //TODO 加载索引到本地文件中 还要做时间的优化
//        index.save();
//        System.out.println("索引制作完成，时间为："+(end-begin)+"ms");
//    }

    private void runByThread() throws IOException, InterruptedException  {
        //要进行构建方法
        System.out.println("索引制作开始");
        long begin=System.currentTimeMillis();
        List<File> fileList=new ArrayList<>();
        enumFile(INPUT_PATH,fileList);
        //使用线程池来完成
        ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(100, 20, 200, TimeUnit.SECONDS, null);
        ExecutorService executorService= Executors.newFixedThreadPool(8);
        //在这里不要进行加锁，因为这里不是造成线程不安全的主要原因
        //这里加速的话 ，会造成锁的粒度加大
        //因为是修改的是不同的东西或者是对象
        CountDownLatch latch=new CountDownLatch(fileList.size());
        for(File file:fileList) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始解析："+file.getAbsolutePath());
                    try {
                        parseHTML(file);
                        latch.countDown();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
        long end=System.currentTimeMillis();
        //将每个文件构建完成的时候，才不会等待
        latch.await();
        //固定的线程数量，为不是后台线程，就会影响到进程的结束，也就是他们不结束，进程不结束
        //而线程池设置 出来的线程不是后台线程，setDaemon，线程池没有这个办法
        //main结束后，线程并不会直接销毁，而是一直在等待，新的任务到来。
        executorService.shutdown();
        index.save();
        System.out.println("索引制作完成，时间为："+(end-begin)+"ms");
    }
    private void enumFile(String path, List<File> fileList) {
        //1.先将每个文件解析出来，将文件添加到这个列表中
        //2.然后再根据词进行，构建正序索引
        File rootFile=new File(path);
        File[] listFiles = rootFile.listFiles();
        for(File file: listFiles) {
            if(file.isDirectory()) {
                //进行递归,这里得到的是一个绝对路径，是一个String类型
                enumFile(file.getAbsolutePath(),fileList);
            } else {
                if(file.getName().endsWith(".html")) {
                    //以html结尾的话，将一些字体文件的给排除在外面
                    fileList.add(file);
                }
            }
        }
    }
    public static void main(String[] args) throws IOException, InterruptedException {
        Parse parse=new Parse();
        //parse.run();
        parse.runByThread();
    }
}
